package com.pig4cloud.pigx.common.customize.redis.utils;

import com.pig4cloud.pigx.common.customize.redis.constants.CommonRedisConstants;
import com.pig4cloud.pigx.common.customize.redis.constants.RedisKeyEnum;
import lombok.AllArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Redis工具类
 *
 * @since 2022/8/16 10:51
 */
@Component
@AllArgsConstructor
public class RedisUtils {

    private final RedisTemplate redis;
    private final RedissonClient redisson;

    /**
     * 争夺分布式锁
     *
     * @param key    锁名
     * @param expire 过期时间，不指定时为redisson默认的30秒
     * @return {@link RLock} 返回值表示成功获取到锁，返回null表示未能获取到锁
     * @since 2022/8/16 10:54
     */
    public RLock getLock(String key, Long expire) {
        String lockKey = getLockKey(key);

        RLock lock = redisson.getLock(lockKey);
        if (Objects.isNull(expire)) {
            return lock.tryLock() ? lock : null;

        } else {
            try {
                return lock.tryLock(0L, expire, TimeUnit.MILLISECONDS) ? lock : null;
            } catch (InterruptedException e) {
            }
        }

        return null;
    }

    /**
     * 争夺分布式锁
     *
     * @param key    锁名
     * @param expire 过期时间，不指定时为redisson默认的30秒
     * @return {@link RLock}
     * @since 2022/8/16 10:54
     */
    public RLock waitLock(String key, Long expire) {
        String lockKey = getLockKey(key);

        RLock lock = redisson.getLock(lockKey);
        if (Objects.isNull(expire)) {
            lock.lock();
        } else {
            lock.lock(expire, TimeUnit.MILLISECONDS);
        }

        return lock;
    }


    /**
     * 释放占用的分布式锁
     *
     * @param lock 锁名
     * @since 2022/8/16 10:58
     */
    public void releaseLock(RLock lock) {
        if (!Objects.isNull(lock) && lock.isLocked()) {
            lock.unlock();
        }
    }

    /**
     * 获得锁key
     *
     * @param key key
     * @return {@link String}
     * @since 2022/8/27 18:38
     */
    public static String getLockKey(String key) {
        return CommonRedisConstants.LOCK_POOL + ":" + key;
    }

    /**
     * 如果没有设置过期时间，设置过期时间
     *
     * @param key      key
     * @param timeout  时间数量
     * @param timeUnit 时间单位
     * @since 2022/11/11 16:20
     */
    public void setExpireIfNotSet(String key, long timeout, TimeUnit timeUnit) {
        Long expire = redis.getExpire(key);
        if (Objects.isNull(expire) || expire == -1) {
            redis.expire(key, timeout, timeUnit);
        }
    }

    /**
     * 更新持久化缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @param value   value
     * @since 2022/11/24 14:10
     */
    public void flushPersistentCache(RedisKeyEnum hashKey, Object key, Object value) {
        redis.opsForHash().put(hashKey.getPersistentCacheKey(), key.toString(), value);
    }

    /**
     * 删除持久化缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @since 2022/11/24 14:10
     */
    public void delPersistentCache(RedisKeyEnum hashKey, Object key) {
        redis.opsForHash().delete(hashKey.getPersistentCacheKey(), key.toString());
    }

    /**
     * 获取持久化缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @param type    type
     * @return {@link T}
     * @since 2022/11/24 14:10
     */
    public <T> T getPersistentCache(RedisKeyEnum hashKey, Object key, Class<T> type) {
        Object obj = redis.opsForHash().get(hashKey.getPersistentCacheKey(), key.toString());
        if (Objects.isNull(obj)) {
            return null;
        }

        return (T) obj;
    }

    /**
     * 获取持久化缓存
     *
     * @param hashKey hashKey
     * @param keys    keys
     * @return {@link List}
     * @since 2022/11/24 14:10
     */
    public List getPersistentCache(RedisKeyEnum hashKey, Set<?> keys) {

        keys = keys.stream()
                .map(Object::toString)
                .collect(Collectors.toSet());

        List<Object> list = redis.opsForHash().multiGet(hashKey.getPersistentCacheKey(), keys);
        if (Objects.isNull(list)) {
            throw new RuntimeException();
        }

        return list.stream()
                .filter(i -> !Objects.isNull(i))
                .collect(Collectors.toList());

    }

    /**
     * 获取持久化缓存
     *
     * @param hashKey hashKey
     * @return {@link List}
     * @since 2022/11/24 14:10
     */
    public List getPersistentCache(RedisKeyEnum hashKey) {

        List<Object> list = redis.opsForHash().values(hashKey.getPersistentCacheKey());
        if (Objects.isNull(list)) {
            throw new RuntimeException();
        }

        return list;

    }

    /**
     * 增加自增缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @param step    step
     * @return {@link long}
     * @since 2022/11/24 14:10
     */
    public long addIncrCache(RedisKeyEnum hashKey, Object key, long step) {
        return redis.opsForHash().increment(hashKey.getIncrCacheKey(), key.toString(), step);
    }

    /**
     * 增加自增缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @param step    step
     * @return {@link double}
     * @since 2022/11/24 14:10
     */
    public double addIncrCache(RedisKeyEnum hashKey, Object key, double step) {
        return redis.opsForHash().increment(hashKey.getIncrCacheKey(), key.toString(), step);
    }

    /**
     * 删除自增缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @since 2022/11/24 14:10
     */
    public void delIncrCache(RedisKeyEnum hashKey, Object key) {
        redis.opsForHash().delete(hashKey.getIncrCacheKey(), key.toString());
    }

    /**
     * 获取自增缓存
     *
     * @param hashKey hashKey
     * @param key     key
     * @param type    type
     * @return {@link T}
     * @since 2022/11/24 14:10
     */
    public <T> T getIncrCache(RedisKeyEnum hashKey, Object key, Class<T> type) {
        Object obj = redis.opsForHash().get(hashKey.getIncrCacheKey(), key.toString());
        if (Objects.isNull(obj)) {
            return null;
        }

        return (T) obj;
    }

    /**
     * 获取币对价格
     *
     * @param symbolName 币对名称
     * @param priceType  币对类型 来源 => CommonRedisConstants
     * @return 价格
     */
    public BigDecimal getSymbolPrice(String symbolName, String priceType) {
        try {
            return new BigDecimal(Objects.requireNonNull(redis.opsForValue().get(priceType + symbolName)).toString());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取币种/USDT价格
     *
     * @param currencyName 币种名称 目前只有 UNT =>  WalletConstant.UNT
     * @return 价格
     */
    public BigDecimal getCurrencyPrice(String currencyName) {
        try {
            return new BigDecimal(Objects.requireNonNull(redis.opsForValue().get(CommonRedisConstants.LAST_PRICE + currencyName)).toString());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 发布消息
     *
     * @param key key
     * @param msg 消息
     * @since 2022/12/13 17:38
     */
    public void publishMsg(RedisKeyEnum key, Object msg) {
        // 从队列左侧存入
        redis.opsForList().leftPush(key.getHashKey(), msg);
    }

    /**
     * 获取消息
     *
     * @param key key
     * @since 2022/12/13 17:38
     */
    public <T> T getMsg(RedisKeyEnum key, Class<T> type) {
        // 从队列右侧取出
        Object obj = redis.opsForList().rightPop(key.getHashKey());
        if (Objects.isNull(obj)) {
            return null;
        }

        return (T) obj;
    }

}
